unit NMedia;

interface
uses Windows, Graphics, SysUtils, NUtils, NCore;

 //-------------------------------------
 // External functions
 //-------------------------------------
 procedure NMediaGetInfo(var info: TNLibraryInfo);
 
 const DefaultCompressionLevel = 6;

type
  TNImageFormat = class(TObject)
    private
      hHandle: Pointer;
      constructor Create(handle: Pointer); overload;
    public
      class function  GetFormatCount: Integer;

      class function  CreateFormat(index: Integer): TNImageFormat;
      class function  CreateBmpFormat: TNImageFormat;

      class procedure GetImageFormats(var openFileFormats: string; var saveImageFormats: string);

      function CanWrite: Boolean;
      function CanRead: Boolean;
      function FormatName: string;
      function FormatFileFilter: string;
      function DefaultExtension: string;
  end;

  TNImage = class(TObject)
  private
     hHandle: Pointer;
  public
      constructor Create(handle: Pointer); overload;
      destructor Destroy(); override;

      property Handle: Pointer read hHandle;

      class function   CreateFromFile(fileName: string; imageFormat: TNImageFormat): TNImage;
      class function   CreateFromImage(pixelFormat: TNPixelFormat; stride: LongWord; hSrcImage: TNImage): TNImage;
      class function   CreateFromImageEx(pixelFormat: TNPixelFormat; stride: LongWord; horzResolution: Single;
                vertResolution: Single; hSrcImage: TNImage): TNImage;
      class function   CreateFromHBitmap(image: HBitmap): TNImage;

      function CreateColorWrapper(min, max: TNRgb): TNImage;

      function   HorizontalResolution: Single;
      function   VerticalResolution: Single;
      function   Stride: LongWord;
      function   Height: LongWord;
      function   Width:  LongWord;

      function   Clone: TNImage;

      function   SaveToHBitmap: HBitmap;
      procedure  SaveToFile(fileName: string; hImageFormat: TNImageFormat);

      function   Crop(left, top, width, height: LongInt): TNImage;
  end;

  TNPng = class(TObject)
    private

    public

     class function LoadImage(fileName: string): TNImage;  overload;
     class function LoadImage(arr: TArrayOfByte): TNImage; overload;
     class procedure SaveImage(image: TNImage; fileName: string); overload;
     class procedure SaveImage(image: TNImage; compressionLevel: Integer; fileName: string); overload;
     class function SaveImage(image: TNImage): TArrayOfByte; overload;
     class function SaveImage(image: TNImage; compressionLevel: Integer): TArrayOfByte; overload;
  end;

  TArrayOfTNImage = array of TNImage;

implementation
 //-------------------------------------
 // Constants
 //-------------------------------------

 const
  dllName = 'NMedia.dll';

 //-------------------------------------
 // External functions (from dll)
 //-------------------------------------
 {$ifdef ver210}
 function  NMediaGetInfoInternal(var info: TNLibraryInfo): Integer stdcall; external dllName name 'NMediaGetInfoW';
 {$else}
 function  NMediaGetInfoInternal(var info: TNLibraryInfo): Integer stdcall; external dllName name 'NMediaGetInfoA';
 {$endif}
 function  NImagesGetGrayscaleColorWrapperEx(hImage: Pointer; var minColor: TNRgb; var maxColor: TNRgb; var pHDstImage: Pointer): Integer stdcall; external dllName;

 {$ifdef ver210}
 function  NImageCreateFromFileEx(const szFileName: string; hImageFormat: Pointer; flags: Integer; var pHImageInfo: Pointer; var pHImage: Pointer): Integer stdcall;external dllName name 'NImageCreateFromFileExW';
 function  NImageSaveToFileEx(hImage: Pointer; szFileName: PChar; hImageFormat: Pointer; hImageInfo: Pointer; flags: Integer): Integer stdcall; external dllName name 'NImageSaveToFileExW';
 {$else}
 function  NImageCreateFromFileEx(const szFileName: string; hImageFormat: Pointer; flags: Integer; var pHImageInfo: Pointer; var pHImage: Pointer): Integer stdcall;external dllName name 'NImageCreateFromFileExA';
 function  NImageSaveToFileEx(hImage: Pointer; szFileName: PChar; hImageFormat: Pointer; hImageInfo: Pointer; flags: Integer): Integer stdcall; external dllName name 'NImageSaveToFileExA';
 {$endif}
 function  NImageCreateFromHBitmap(handle: HBitmap; flags: Integer; var hImage: Pointer): Integer stdcall; external dllName;
 function  NImageCreateFromImageEx(pixelFormat: TNPixelformat; stride: Integer; horzResolution: Single; vertResolution: Single; hSrcImage: Pointer; var pHImage: Pointer): Integer stdcall; external dllName;
 function  NImageCreateFromImageEx2(pixelFormat: TNPixelFormat; stride: LongWord; hSrcImage: Pointer; flags: Integer; var pHImage: Pointer): Integer; stdcall; external dllName;

 function  NImageToHBitmap(hImage: Pointer; var pHandle: HBitmap): Integer stdcall; external dllName;

 function  NImageGetHorzResolution(hImage: Pointer; var pValue: Single): Integer stdcall;external dllName;
 function  NImageGetVertResolution(hImage: pointer; var pValue: Single): Integer stdcall; external dllName;
 function  NImageGetWidth(hImage: Pointer; var pValue: LongWord): Integer stdcall;external dllName;
 function  NImageGetHeight(hImage: Pointer; var pValue: LongWord): Integer stdcall;external dllName;
 function  NImageGetStride(hImage: Pointer; var pValue: LongWord): Integer stdcall; external dllName;
 function  NImageCrop(hImage: Pointer; left: Integer; top: Integer; width: Integer; height: Integer; var hHandle: Pointer) : Integer stdcall; external dllName;

 {$ifdef ver210}
 function  NImageFormatGetFileFilterEx(hImageFormat: Pointer; value: PChar; valueSize: LongWord): Integer stdcall;external dllName name 'NImageFormatGetFileFilterExW';
 function  NImageFormatGetNameEx(hImageFormat: Pointer; value: PChar; valueSize: LongWord): Integer stdcall; external dllName name 'NImageFormatGetNameExW';
 function  NImageFormatGetDefaultFileExtensionEx(hImageFormat: Pointer; value: PChar; valueSize: LongWord): Integer stdcall; external dllName name 'NImageFormatGetDefaultFileExtensionExW';
 {$else}
 function  NImageFormatGetFileFilterEx(hImageFormat: Pointer; value: PChar; valueSize: LongWord): Integer stdcall;external dllName name 'NImageFormatGetFileFilterExA';
 function  NImageFormatGetNameEx(hImageFormat: Pointer; value: PChar; valueSize: LongWord): Integer stdcall; external dllName name 'NImageFormatGetNameExA';
 function  NImageFormatGetDefaultFileExtensionEx(hImageFormat: Pointer; value: PChar; valueSize: LongWord): Integer stdcall; external dllName name 'NImageFormatGetDefaultFileExtensionExA';
 {$endif}
 function  NImageFormatGetFormatCount(int: Pointer): Integer stdcall;external dllName;
 function  NImageFormatCanRead(hImageFormat: Pointer; value: Pointer): Integer stdcall;external dllName;
 function  NImageFormatCanWrite(hImageFormat: Pointer; value: Pointer): Integer stdcall;external dllName;
 function  NImageFormatGetFormat(index: Integer; var hImageFormat: Pointer): Integer stdcall;external dllName;
 function  NImageFormatGetBmp(var pValue: Pointer): Integer stdcall; external dllName;

 {$ifdef ver210}
 function  PngLoadImageFromFile(const szFileName: string; var pHImage: Pointer): Integer stdcall; external dllName name 'PngLoadImageFromFileW';
 function  PngSaveImageToFile(hImage: Pointer; compressionLevel: Integer; szFileName: string): Integer stdcall; external dllName name 'PngSaveImageToFileW';
 {$else}
 function  PngLoadImageFromFile(const szFileName: string; var pHImage: Pointer): Integer stdcall; external dllName name 'PngLoadImageFromFileA';
 function  PngSaveImageToFile(hImage: Pointer; compressionLevel: Integer; szFileName: string): Integer stdcall; external dllName name 'PngSaveImageToFileA';
 {$endif}
 function  PngLoadImageFromMemory(buffer: Pointer; size: LongWord; var pHImage: Pointer): Integer stdcall; external dllName;
 function  PngSaveImageToMemory(hImage: Pointer; compressionLevel: Integer; var buffer: Pointer; var size: LongWord): Integer stdcall; external dllName;
 //-------------------------------------
 // Realization of functions
 //-------------------------------------

 procedure NMediaGetInfo(var info: TNLibraryInfo);
  var res: Integer;
 begin
  res := NMediaGetInfoInternal(info);
  NCheck(res);
 end;

 //-------------------------------------
 // Realization of TNImage functions
 //-------------------------------------

 constructor TNImage.Create(handle: Pointer);
 begin
  inherited Create;
  hHandle := handle;
 end;

 function TNImage.CreateColorWrapper(min, max: TNRgb): TNImage;
  var imagePtr: Pointer;
      res: Integer;
 begin
  res := NImagesGetGrayscaleColorWrapperEx(hHandle, min, max, imagePtr);
  NCheck(res);
  Result := TNImage.Create(imagePtr);
 end;

 class function TNImage.CreateFromFile(fileName: string; imageFormat: TNImageFormat): TNImage;
  var res: Integer;
      handle: Pointer;
      tmpHandle, tmpImageInfo: Pointer;
 begin
  tmpHandle := nil;
  if (imageFormat <> nil) then
   tmpHandle := imageFormat.hHandle;

  res := NImageCreateFromFileEx(filename, tmpHandle, 0, tmpImageInfo, handle);
  NCheck(res);
  Result := TNImage.Create(handle);
 end;

 class function TNImage.CreateFromImageEx(pixelFormat: TNPixelFormat; stride: LongWord; horzResolution: Single;
                vertResolution: Single; hSrcImage: TNImage): TNImage;
  var pHImage: Pointer;
      ret: Integer;
 begin
     pHImage := nil;
     ret := NImageCreateFromImageEx(pixelFormat, stride, horzResolution, vertResolution, hSrcImage.Handle, pHImage);
     NCheck(ret);
     Result := TNImage.Create(pHImage);
 end;
 
 class function TNImage.CreateFromImage(pixelFormat: TNPixelFormat; stride: LongWord; hSrcImage: TNImage): TNImage;
  var res: Integer;
      output: Pointer;
 begin
  res := NImageCreateFromImageEx2(pixelFormat, stride, hSrcImage.Handle, 0, output);
  NCheck(res);
  Result := TNImage.Create(output);
 end;

class function TNImage.CreateFromHBitmap(image: HBitmap): TNImage;
  var res: Integer;
      handle: Pointer;
 begin
  Handle := nil;
  res := NImageCreateFromHBitmap(image, 0, handle);
  NCheck(res);
  Result := TNImage.Create(handle);
 end;

 function TNImage.SaveToHBitmap: HBitmap;
  var res: Integer;
      handle: HBitmap;
 begin
  res := NImageToHBitmap(self.hHandle, handle);
  NCheck(res);
  Result := handle;
 end;

 destructor TNImage.Destroy();
 begin
  if (hHandle <> nil) then
  begin
   NObjectFree(hHandle);
   hHandle := nil;
  end
 end;

 function TNImage.Clone: TNImage;
  var image: Pointer;
 begin
  NObjectClone(hHandle, image);
  Result := TNImage.Create(image);
 end;
 
 function TNImage.HorizontalResolution: Single;
  var ret: Integer;
      horzRes: Single;
 begin
  horzRes := 0;
  ret := NImageGetHorzResolution(hHandle, horzRes);
  NCheck(ret);
  Result := horzRes;
 end;

 function TNImage.VerticalResolution: Single;
  var ret: Integer;
      vertRes: Single;
 begin
   vertRes := 0;
   ret := NImageGetVertResolution(hHandle, vertRes);
   NCheck(ret);
   Result := vertRes;
 end;

 function TNImage.Width: LongWord;
  var res:Integer;
      wdth: LongWord;
 begin
  res   := NImageGetWidth(hHandle, wdth);
  NCheck(res);
  Result := wdth;
 end;

 function TNImage.Height: LongWord;
  var res: Integer;
      hght: LongWord;
 begin
  res    := NImageGetHeight(hHandle, hght);
  NCheck(res);
  Result := hght;
 end;

 function TNImage.Stride: LongWord;
  var res: Integer;
      strd: LongWord;
 begin
  res := NImageGetStride(hHandle, strd);
  NCheck(res);
  Result := strd;
 end;

 procedure TNImage.SaveToFile(fileName: string; hImageFormat: TNImageFormat);
  var res: Integer;
      handle: Pointer;
 begin
  handle := nil;
  if (hImageFormat <> nil) then
    handle := hImageFormat.hHandle;
  res := NImageSaveToFileEx(hHandle, PChar(FileName), handle, nil, 0);
  NCheck(res);
 end;

 function TNImage.Crop(left: Integer; top: Integer; width: Integer; height: Integer): TNImage;
  var res: Integer;
      handle: Pointer;
 begin
  res := NImageCrop(hHandle, left, top, width, height, handle);
	NCheck(res);
  Result := TNImage.Create(handle);
 end;

 //-------------------------------------
 // Realization of TNImageFormat functions
 //-------------------------------------

 constructor TNImageFormat.Create(handle: Pointer);
 begin
   hHandle := handle;
 end;

 class function TNImageFormat.CreateFormat(index: Integer): TNImageFormat;
  var res: Integer;
      tmp: Pointer;
 begin
  res := NImageFormatGetFormat(index, tmp);
  NCheck(res);
  Result := TNImageFormat.Create(tmp);
 end;

 class function TNImageFormat.CreateBmpFormat: TNImageFormat;
  var res: Integer;
      tmpPointer: Pointer;
 begin
  res := NImageFormatGetBmp(tmpPointer);
  NCheck(res);
  Result := TNImageFormat.Create(tmpPointer);
 end;

 class function TNImageFormat.GetFormatCount: Integer;
  var tmp, res: Integer;
 begin
  tmp := 0;
  res := NImageFormatGetFormatCount(@tmp);
  NCheck(res);
  Result := tmp;
 end;

 function TNImageFormat.CanRead: Boolean;
  var res: Integer;
      tmp: Integer;
 begin
  Result := false;
  tmp := 0;
  res := NImageFormatCanRead(hHandle, @tmp);
  NCheck(res);
  if (tmp = 1) then Result := true;
 end;

 function TNImageFormat.CanWrite: Boolean;
  var tmp: Integer;
 begin
  Result := false;
  tmp := 0;
  NCheck(NImageFormatCanWrite(hHandle, @tmp));
  if (tmp = 1) then Result := true;
 end;

 function TNImageFormat.FormatName: string;
  var res: Integer;
      tmp: PChar;
 begin
  Result := '';
  res := NImageFormatGetNameEx(hHandle, nil, 0);
  NCheck(res);
  GetMem(tmp, (res + 1) * SizeOf(PChar));
  NCheck(NImageFormatGetNameEx(hHandle, tmp, res + 1));
  Result := String(tmp);
  FreeMem(tmp);
 end;

 function TNImageFormat.FormatFileFilter: string;
  var res: Integer;
     tmp: PChar;
 begin
  Result := '';
  res := NImageFormatGetFileFilterEx(hHandle, nil, 0);
  NCheck(res);
  GetMem(tmp, (res + 1) * SizeOf(PChar));
  NCheck(NImageFormatGetFileFilterEx(hHandle, tmp, res + 1));
  Result := String(tmp);
  FreeMem(tmp);
 end;

 function TNImageFormat.DefaultExtension: string;
  var res: Integer;
     tmp: PChar;
 begin
  Result := '';
  res := NImageFormatGetDefaultFileExtensionEx(hHandle, nil, 0);
  NCheck(res);
  GetMem(tmp, (res + 1) * SizeOf(PChar));
  NCheck(NImageFormatGetDefaultFileExtensionEx(hHandle, tmp, res + 1));
  Result := String(tmp);
  FreeMem(tmp);
 end;

 class procedure TNImageFormat.GetImageFormats(var openFileFormats: string; var saveImageFormats: string);
  var i, count: Integer;
      imageFormat: TNImageFormat;
      openAllImageFormats: string;
 begin
  openAllImageFormats := '';
  count := TNImageFormat.GetFormatCount - 1;
  for i := 0 to count do
  begin
   imageFormat := TNImageFormat.CreateFormat(i);
   if (imageFormat.CanRead) then
   begin
    openFileFormats := openFileFormats + '|';
    openFileFormats := openFileFormats + Format('%s files (%s)|%s',[imageFormat.FormatName,
     imageFormat.FormatFileFilter, imageFormat.FormatFileFilter]);
    if (Length(openAllImageFormats) <> 0) then
        openAllImageFormats := openAllImageFormats + ';';
    openAllImageFormats := openAllImageFormats + imageFormat.FormatFileFilter;
   end;
   if (imageFormat.CanWrite) then
   begin
     if (Length(saveImageFormats) <> 0) then
      saveImageFormats := saveImageFormats + '|';
     saveImageFormats := saveImageFormats + Format('%s files (%s)|%s',[imageFormat.FormatName, imageFormat.FormatFileFilter, imageFormat.FormatFileFilter]);
   end;
  end;
  openFileFormats := Format('All supported images (%s)|%s', [openAllImageFormats, openAllImageFormats]) + openFileFormats;
  openFileFormats := openFileFormats + '|All files (*.*)|*.*';
 end;

 //-------------------------------------
 // Realization of TNPgd functions
 //-------------------------------------

 class function TNPng.LoadImage(fileName: string): TNImage;
  var res: Integer;
      hPointer: Pointer;
 begin
  res := PngLoadImageFromFile(fileName, hPointer);
  NCheck(res);
  Result := TNImage.Create(hPointer);
 end;

 class function TNPng.LoadImage(arr: TArrayOfByte): TNImage;
  var res: Integer;
      hPointer: Pointer;
 begin
  res := PngLoadImageFromMemory(@(arr[0]), Length(arr), hPointer);
  NCheck(res);
  Result := TNImage.Create(hPointer);
 end;

 class procedure TNPng.SaveImage(image: TNImage; fileName: string);
 begin
  SaveImage(image, DefaultCompressionLevel, fileName);
 end;

 class procedure TNPng.SaveImage(image: TNImage; compressionLevel: Integer; fileName: string);
  var res: Integer;
 begin
   res := PngSaveImageToFile(image.hHandle, compressionLevel, fileName);
   NCheck(res);
 end;

 class function TNPng.SaveImage(image: TNImage): TArrayOfByte;
 begin
  Result := SaveImage(image, DefaultCompressionLevel);
 end;

 class function TNPng.SaveImage(image: TNImage; compressionLevel: Integer): TArrayOfByte;
  var res: Integer;
      hPointer: Pointer;
      arr: TArrayOfByte;
      size: LongWord;
 begin
   res := PngSaveImageToMemory(image.hHandle, compressionLevel, hPointer, size);
   NCheck(res);
   SetLength(arr, size);
   CopyMemory(@(arr[0]), hPointer, size);
   NFree(hPointer);
   Result := arr;
 end;
 
end.
